/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using DaveSexton.DocProject.MSBuild;
using System.ComponentModel.Design;
using DaveSexton.DocProject.Configuration;

namespace DaveSexton.DocProject.ExternalUI
{
	[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1301:AvoidDuplicateAccelerators")]
	public sealed partial class ProgramForm : Form
	{
		#region Public Properties
		public string StartupProjectFile
		{
			get
			{
				return startupProjectFile;
			}
			set
			{
				if (string.IsNullOrEmpty(value))
					startupProjectFile = null;
				else
					startupProjectFile = value;
			}
		}

		public bool IsDirty
		{
			get
			{
				return manager.IsDirty;
			}
			set
			{
				manager.IsDirty = value;
			}
		}

		public string Status
		{
			get
			{
				return buildToolStripStatusLabel.Text;
			}
			set
			{
				buildToolStripStatusLabel.Text = value;
			}
		}

		public int BuildTraceHeight
		{
			get
			{
				return buildSplitter.Height - buildSplitter.SplitterDistance;
			}
			set
			{
				buildSplitter.SplitterDistance = buildSplitter.Height - value;
			}
		}

		public MessageBoxOptions MessageBoxOptions
		{
			get
			{
				if (RightToLeft == RightToLeft.Yes)
					return MessageBoxOptions.RightAlign | MessageBoxOptions.RtlReading;
				else
					return (MessageBoxOptions) 0;
			}
		}

		public string SelectedConfiguration
		{
			get
			{
				return (string) configurationToolStripComboBox.SelectedItem;
			}
		}

		public string SelectedPlatform
		{
			get
			{
				return (string) platformToolStripComboBox.SelectedItem;
			}
		}
		#endregion

		#region Private / Protected
		internal ProgramServices Services
		{
			get
			{
				return services;
			}
		}

		internal ProjectManager ProjectManager
		{
			get
			{
				return manager;
			}
		}

		private string startupProjectFile;
		private bool building, exitAfterBuild, exitSilentlyAfterBuild, initializingBuildConfiguration;

		/// <remarks>
		/// Although only one project is loaded at a time, the <see cref="ProgramServicesContainer"/> class provides an 
		/// <see cref="IComponentChangeService"/> implementation that is used by the project's <see cref="DocProjectOptions"/> 
		/// for the <see cref="PropertyGrid"/> control, which will cause the <see cref="PropertyGrid.PropertyValueChanged"/> 
		/// event to be raised and the program's tool bars and menu items to be synchronized automatically; 
		/// e.g., <strong>Save</strong> and <strong>Save As</strong>.
		/// <para>
		/// Change notification is only required for properties that have a custom editor and never actually change the
		/// property's return value; otherwise, the <see cref="PropertyGrid"/> detects changes automatically.
		/// </para>
		/// <para>
		/// For this to work correctly, types derived from <see cref="DocProjectOptions"/> must obtain a reference to the 
		/// <see cref="IComponentChangeService"/> implementation by checking if the <see cref="Component.Site"/> is not null
		/// and then calling the <see cref="ISite.GetService"/> method.  If a property has changed and the interface can
		/// be obtained then the <see cref="IComponentChangeService.OnComponentChanged"/> method should be invoked.
		/// </para>
		/// </remarks>
		private readonly ProgramServicesContainer serviceContainer;
		private readonly ProjectManager manager;
		private readonly ProgramServices services;
		private readonly ProgramSettings settings;
		private readonly FormBuildTrace trace;
		private readonly MSBuildLogger logger;
		private readonly List<ToolStrip> providerToolStrips = new List<ToolStrip>();
		private readonly ComponentResourceManager formResources = new ComponentResourceManager(typeof(ProgramForm));
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="ProgramForm" /> class.
		/// </summary>
		public ProgramForm()
		{
			InitializeComponent();

			services = new ProgramServices(this);

			serviceContainer = new ProgramServicesContainer(services);
			
			manager = new ProjectManager(this, services);
			manager.ProjectClosed += new EventHandler<DocProjectEventArgs>(manager_ProjectClosed);
			manager.BuildComplete += new EventHandler<DocProjectEventArgs>(manager_BuildComplete);
			manager.IsDirtyChanged += new EventHandler<DocProjectEventArgs>(manager_IsDirtyChanged);

			settings = new ProgramSettings(this);
			trace = new FormBuildTrace(this);
			logger = new MSBuildLogger(this);

			// NOTE: Visual Studio is terrible when it comes to designing toolstrips - they are automatically rearranged every time the designer is opened.
			// The workaround is to explicitly re-join them in reverse order (from right to left): 
			buildConfigurationToolStrip.Parent.Controls.Remove(buildConfigurationToolStrip);
			viewToolStrip.Parent.Controls.Remove(viewToolStrip);
			standardToolStrip.Parent.Controls.Remove(standardToolStrip);
			buildToolStrip.Parent.Controls.Remove(buildToolStrip);

			toolStrips.TopToolStripPanel.Join(buildConfigurationToolStrip, 1);
			toolStrips.TopToolStripPanel.Join(viewToolStrip, 1);
			toolStrips.TopToolStripPanel.Join(buildToolStrip, 1);
			toolStrips.TopToolStripPanel.Join(standardToolStrip, 1);
		}
		#endregion

		#region Methods
		public bool OpenProject()
		{
			if (manager.OpenProject())
			{
				LoadProject();
				return true;
			}

			return false;
		}

		public bool OpenProject(string projectFile)
		{
			if (projectFile == null)
				throw new ArgumentNullException("projectFile");

			if (manager.OpenProject(projectFile))
			{
				LoadProject();
				return true;
			}

			return false;
		}

		public void SaveProject()
		{
			manager.SaveProject();

			SynchronizeUIForProject();

			// Some properties use comitted data as the default values so once a bold property is saved it
			// may appear regular by refreshing the grid.
			propertyGrid.Refresh();
		}

		private void SaveProjectAs()
		{
			if (manager.SaveProjectAs())
				LoadProject();
		}

		public bool CloseProject(bool forceSave)
		{
			if (!manager.CloseProject(forceSave))
				return false;

			if (manager.Project != null)
			{
				DocProjectOptions options = manager.Project.Options;

				if (options != null)
					serviceContainer.Remove(options);
			}

			propertyGrid.SelectedObject = null;

			Text = formResources.GetString("$this.Text");

			SynchronizeUIForProject();

			return true;
		}

		private void LoadProject()
		{
			MSBuildDocProject project = manager.Project;

			if (project != null)
			{
				LoadConfigurationsAndPlatforms();

				serviceContainer.Add(project.Options);

				propertyGrid.SelectedObject = project.Options;

				// this must be set before creating the provider's toolbars
				DocProjectEnvironment.SelectedProject = project;

				InitializeProviderToolBars(project.Provider);
			}

			SynchronizeUIForProject();
		}

		private void LoadConfigurationsAndPlatforms()
		{
			initializingBuildConfiguration = true;

			try
			{
				foreach (KeyValuePair<string, string> pair in manager.ConfigurationPlatformPairs)
				{
					string configuration = pair.Key;

					if (!string.IsNullOrEmpty(configuration) && !configurationToolStripComboBox.Items.Contains(configuration))
						configurationToolStripComboBox.Items.Add(pair.Key);

					string platform = pair.Value;

					if (!string.IsNullOrEmpty(platform) && !platformToolStripComboBox.Items.Contains(platform))
						platformToolStripComboBox.Items.Add(pair.Value);
				}

				SelectProjectConfigurationAndPlatform();
			}
			finally
			{
				initializingBuildConfiguration = false;
			}
		}

		private void SelectProjectConfigurationAndPlatform()
		{
			string configuration = manager.Configuration;
			string platform = manager.Platform;

			if (string.IsNullOrEmpty(configuration))
			{
				if (configurationToolStripComboBox.Items.Count > 0)
					configurationToolStripComboBox.SelectedIndex = 0;
			}
			else
				configurationToolStripComboBox.SelectedItem = configuration;

			if (string.IsNullOrEmpty(platform))
			{
				if (configurationToolStripComboBox.Items.Count > 0)
					platformToolStripComboBox.SelectedIndex = 0;
			}
			else
				platformToolStripComboBox.SelectedItem = platform;
		}

		private void SynchronizeUIForProject()
		{
			SuspendLayout();

			MSBuildDocProject project = manager.Project;

			bool hasProject = project != null;

			closeToolStripMenuItem.Enabled = hasProject;
			saveAsToolStripMenuItem.Enabled = hasProject;
			buildToolStripMenuItem.Visible = hasProject;
			buildItemsToolStripMenuItem.Visible = hasProject;
			buildToolStripButton.Enabled = hasProject;
			openInVisualStudioSeparator.Visible = hasProject;
			openInVisualStudioToolStripMenuItem.Visible = hasProject;
			openInVisualStudioToolStripButton.Enabled = hasProject;
			configurationToolStripComboBox.Enabled = hasProject;
			platformToolStripComboBox.Enabled = hasProject;
			buildItemsToolStripButton.Enabled = hasProject;

			if (hasProject)
			{
				buildHelpToolStripMenuItem.Visible = true;
				cancelToolStripMenuItem.Visible = false;

				projectPathToolStripStatusLabel.Text = project.Directory;

				SynchronizeUIForDirtyState();
			}
			else
			{
				saveToolStripMenuItem.Enabled = false;
				saveToolStripButton.Enabled = false;

				projectPathToolStripStatusLabel.Text = null;
			}

			ResumeLayout(true);
		}

		private void SynchronizeUIForBuild()
		{
			SuspendLayout();

			if (building)
			{
				Status = "Preparing to build...";

				buildTraceTextBox.Clear();

				ShowBuildTrace();

				progress.Value = 0;
				progress.Maximum = 0;
				progress.Visible = true;

				saveToolStripMenuItem.Enabled = false;

				buildToolStripStatusLabel.Text = null;
			}
			else
			{
				progress.Visible = false;

				// The following menu item is only disabled if the build was canceled
				buildToolStripMenuItem.Enabled = true;

				// building the project automatically saves it first, so the dirty state should be updated
				SynchronizeUIForDirtyState();
			}

			projectPathToolStripStatusLabel.Visible = !building;

			buildToolStripStatusLabel.Visible = building;

			configurationToolStripComboBox.Enabled = !building;
			platformToolStripComboBox.Enabled = !building;

			standardToolStrip.Enabled = !building;
			closeToolStripMenuItem.Enabled = !building;
			openToolStripMenuItem.Enabled = !building;
			saveAsToolStripMenuItem.Enabled = !building;

			openInVisualStudioToolStripMenuItem.Enabled = !building;
			openInVisualStudioToolStripButton.Enabled = !building;

			buildToolStripButton.Enabled = !building;
			cancelToolStripButton.Enabled = building;

			buildHelpToolStripMenuItem.Visible = !building;
			rebuildHelpToolStripMenuItem.Visible = !building;
			cleanHelpToolStripMenuItem.Visible = !building;
			cancelToolStripMenuItem.Visible = building;

			optionsToolStripMenuItem.Enabled = !building;

			propertyGrid.Enabled = !building;

			foreach (ToolStrip strip in providerToolStrips)
				strip.Enabled = !building;

			ResumeLayout(true);
		}

		private void SynchronizeUIForDirtyState()
		{
			bool dirty = manager.IsDirty;

			saveToolStripMenuItem.Enabled = dirty;
			saveToolStripButton.Enabled = dirty;

			MSBuildDocProject project = manager.Project;

			if (project != null)
			// NOTE: null occurred in testing when this method was called after a build that was canceled because the app was closed by the user
			{
				string caption = (dirty)
					? Resources.Text.ProgramCaptionDirty
					: Resources.Text.ProgramCaption;

				Text = string.Format(System.Globalization.CultureInfo.CurrentCulture, caption, formResources.GetString("$this.Text"), project.Name);
			}
		}

		private void SynchronizePlatform()
		{
			string platform = (string) platformToolStripComboBox.SelectedItem;

			if (manager.Project != null && !string.Equals(manager.Platform, platform, StringComparison.OrdinalIgnoreCase))
				manager.SetPlatform(platform);
		}

		private void SynchronizeConfiguration()
		{
			string configuration = (string) configurationToolStripComboBox.SelectedItem;

			if (manager.Project != null && !string.Equals(manager.Configuration, configuration, StringComparison.OrdinalIgnoreCase))
				manager.SetConfiguration(configuration);
		}

		internal void ShowError(Exception ex, string caption)
		{
			if (ex == null)
				throw new ArgumentNullException("ex");

			ShowError(ex.Message, caption);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions")]
		internal void ShowError(Exception ex, string message, string caption)
		{
			if (ex == null)
				throw new ArgumentNullException("ex");

			MessageBox.Show(this, string.Format(System.Globalization.CultureInfo.CurrentCulture, "{0}{1}{1}{2}: {3}",
				message, Environment.NewLine, ex.GetType(), ex.Message), caption, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions")]
		internal void ShowError(string message, string caption)
		{
			MessageBox.Show(this, message, caption, MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions")]
		internal DialogResult ShowMessage(string message, string caption, MessageBoxButtons buttons)
		{
			return MessageBox.Show(this, message, caption, buttons, MessageBoxIcon.None, MessageBoxDefaultButton.Button1, MessageBoxOptions);
		}

		public void ShowBuildTrace()
		{
			buildTraceToolStripButton.Checked = true;
			buildTraceToolStripMenuItem.Checked = true;
			buildSplitter.Panel2Collapsed = false;
		}

		public void ToggleBuildTrace()
		{
			bool toggle = !buildTraceToolStripMenuItem.Checked;

			buildTraceToolStripButton.Checked = toggle;
			buildTraceToolStripMenuItem.Checked = toggle;
			buildSplitter.Panel2Collapsed = !toggle;
		}

		public void ShowBuildItems()
		{
			using (BuildItemsForm form = new BuildItemsForm(manager.Project.Directory))
			{
				form.Items = manager.Engine.BuildItems;
				form.ShowDialog(this);
			}
		}

		public void Build()
		{
			building = true;

			try
			{
				SynchronizeUIForBuild();

				manager.BuildProject(logger, trace);
			}
			finally
			{
				building = false;

				if (!exitSilentlyAfterBuild)
					SynchronizeUIForBuild();
			}
		}

		public void Rebuild()
		{
			building = true;

			try
			{
				SynchronizeUIForBuild();

				manager.RebuildProject(logger, trace);
			}
			finally
			{
				building = false;

				SynchronizeUIForBuild();
			}
		}

		public void Clean()
		{
			building = true;

			try
			{
				SynchronizeUIForBuild();

				manager.CleanProject(logger);
			}
			finally
			{
				building = false;

				SynchronizeUIForBuild();
			}
		}

		public void CancelBuild()
		{
			manager.CancelBuild();
		}

		private void SaveSettings()
		{
			settings.Save();
		}

		private void LoadSettings()
		{
			settings.Load();
		}

		public void InitializeProviderToolBars(BuildEngineProvider provider)
		{
			if (provider == null)
				throw new ArgumentNullException("provider");

			SuspendLayout();

			foreach (ToolBar toolBar in provider.ToolBars)
			{
				if (toolBar != null)
				{
					ToolStrip strip = new ToolStrip();
					strip.Name = provider.Name + "_" + toolBar.Name;

					foreach (ToolBarCommand command in toolBar.Commands)
					{
						ToolStripButton button = new ToolStripButton(command.Text);
						button.Name = command.UniqueName;
						button.Tag = command;

						UpdateProviderToolBarButton(button, command);

						button.Click += delegate(object sender, EventArgs e)
						{
							ToolStripButton btn = (ToolStripButton) sender;
							ToolBarCommand cmd = (ToolBarCommand) btn.Tag;

							cmd.Execute(EnvDTE.vsCommandExecOption.vsCommandExecOptionDoDefault, serviceContainer);

							UpdateProviderToolBarButton(btn, cmd);
						};

						strip.Items.Add(button);
					}

					Control.ControlCollection controls = toolStrips.TopToolStripPanel.Controls;

					if (controls.Count == 0)
						toolStrips.TopToolStripPanel.Join(strip, 1);
					else
					{
						Rectangle bounds = Rectangle.Empty;

						foreach (Control control in controls)
						{
							if (!(control is MenuStrip) && control.Right > bounds.Right)
								bounds = control.Bounds;
						}

						toolStrips.TopToolStripPanel.Join(strip, bounds.Right, bounds.Y);
					}

					providerToolStrips.Add(strip);
				}
			}

			ResumeLayout(true);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
		public void UpdateProviderToolBars()
		{
			foreach (ToolStrip strip in providerToolStrips)
			{
				foreach (ToolStripItem item in strip.Items)
				{
					ToolStripButton button = item as ToolStripButton;

					if (button != null)
					{
						try
						{
							UpdateProviderToolBarButton(button, (ToolBarCommand) button.Tag);
						}
						catch (Exception ex)
						{
							Status = "Update tool button error: " + ex.Message;
							Log.Exception(ex, Resources.Errors.CannotUpdateToolBarButton, button.Name);
						}
					}
				}
			}
		}

		private static void UpdateProviderToolBarButton(ToolStripButton button, ToolBarCommand command)
		{
			EnvDTE.vsCommandStatus status = command.Status;

			button.ToolTipText = command.Tooltip;
			button.Visible = (status & EnvDTE.vsCommandStatus.vsCommandStatusSupported) != 0;
			button.Enabled = (status & EnvDTE.vsCommandStatus.vsCommandStatusEnabled) != 0;
			button.DisplayStyle = ToolStripItemDisplayStyle.None;

			EnvDTE80.vsCommandStyle style = command.Style;

			if ((style & EnvDTE80.vsCommandStyle.vsCommandStyleText) != 0)
				button.DisplayStyle = ToolStripItemDisplayStyle.Text;

			if ((style & EnvDTE80.vsCommandStyle.vsCommandStylePict) != 0)
			{
				Image image = command.Image;

				if (image != null)
				{
					if (button.DisplayStyle == ToolStripItemDisplayStyle.Text)
					{
						button.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText;
						button.Text = command.Text;
					}
					else
						button.DisplayStyle = ToolStripItemDisplayStyle.Image;

					button.Image = image;
					button.ImageTransparentColor = command.ImageTransparencyKey;
				}
			}
		}

		public void UninitializeProviderToolBars(BuildEngineProvider provider)
		{
			if (provider == null)
				throw new ArgumentNullException("provider");

			SuspendLayout();

			try
			{
				foreach (ToolStrip strip in providerToolStrips)
				{
					if (strip != null)
						strip.Parent.Controls.Remove(strip);
				}

			}
			finally
			{
				providerToolStrips.Clear();

				ResumeLayout(true);
			}
		}

		internal void TraceBuild(string message)
		{
			buildTraceTextBox.AppendText(message);
		}

		internal void TraceBuild(string format, params object[] args)
		{
			if (args == null)
				TraceBuildLine(format);
			else
				buildTraceTextBox.AppendText(string.Format(System.Globalization.CultureInfo.CurrentCulture, format, args));
		}

		internal void TraceBuildLine()
		{
			buildTraceTextBox.AppendText(Environment.NewLine);
		}

		internal void TraceBuildLine(string message)
		{
			buildTraceTextBox.AppendText(message + Environment.NewLine);
		}

		internal void TraceBuildLine(string format, params object[] args)
		{
			if (args == null)
				TraceBuildLine(format);
			else
				buildTraceTextBox.AppendText(string.Format(System.Globalization.CultureInfo.CurrentCulture, format, args) + Environment.NewLine);
		}

		internal void SetBuildProgress(int completed, int total)
		{
			progress.Maximum = total;
			progress.Value = completed;
		}

		private void SynchronizeResetItem(GridItem item)
		{
			bool canReset = false;

			if (item != null && item.GridItemType == GridItemType.Property)
			{
				GridItem parent = item.Parent;

				object obj = null;

				if (parent.GridItemType == GridItemType.Category || parent.GridItemType == GridItemType.Root)
					obj = propertyGrid.SelectedObject;
				else if (parent.GridItemType == GridItemType.Property)
					obj = parent.Value;

				canReset = obj != null && item.PropertyDescriptor.CanResetValue(obj)
						&& item.PropertyDescriptor.ShouldSerializeValue(obj);
			}

			resetToolStripMenuItem.Enabled = canReset;
		}

		private void OpenInVisualStudio()
		{
			if (manager.IsDirty)
			{
				switch (MessageBox.Show(this,
					string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Text.AskSaveProject, Environment.NewLine), Resources.Text.AskSaveProjectCaption,
					MessageBoxButtons.YesNoCancel, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1, MessageBoxOptions))
				{
					case DialogResult.Yes:
						SaveProject();
						break;
					case DialogResult.Cancel:
						return;
				}
			}

			System.Diagnostics.Process.Start(VSEnvironmentHost.DefaultInstance.ProgramPath, 
				"\"" + manager.Project.FilePath + "\"");
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
		protected override void Dispose(bool disposing)
		{
			if (disposing)
			{
				if (components != null)
					components.Dispose();

				if (serviceContainer != null)
					serviceContainer.Dispose();
			}
			base.Dispose(disposing);
		}
		#endregion

		#region Events
		#endregion

		#region Event Handlers
		protected override void OnLoad(EventArgs e)
		{
			LoadSettings();

			if (!string.IsNullOrEmpty(startupProjectFile))
				OpenProject(startupProjectFile);

			if (Site == null || !Site.DesignMode)
				// required so that services such as IComponentChangeService are found by the grid
				serviceContainer.Add(propertyGrid);

			// this service is used to notify the host and property grids when an option's value changes
			IComponentChangeService change = (IComponentChangeService) DocProjectEnvironment.GetService(typeof(IComponentChangeService));

			if (change == null)
				throw new InvalidOperationException();

			change.ComponentChanged += environment_ComponentChanged;

			base.OnLoad(e);
		}

		protected override void OnShown(EventArgs e)
		{
			if (manager.Project == null)
				OpenProject();

			base.OnShown(e);
		}

		protected override void OnFormClosing(FormClosingEventArgs e)
		{
			bool forceSave = e.CloseReason == CloseReason.WindowsShutDown || e.CloseReason == CloseReason.TaskManagerClosing;

			if (building && !exitSilentlyAfterBuild && (!exitAfterBuild		// If the project is being built and the user has not yet been prompted...
				// If the user cancels the build and then attempts to close the form before it actually stops then they 
				// must be prompted again:
				|| e.CloseReason == CloseReason.ApplicationExitCall
				|| e.CloseReason == CloseReason.FormOwnerClosing
				|| e.CloseReason == CloseReason.UserClosing))
			{
				if (forceSave || MessageBox.Show(this,
					string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Text.StopBuild, Environment.NewLine), Resources.Text.StopBuildCaption,
					MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button2, MessageBoxOptions) == DialogResult.OK)
				{
					exitAfterBuild = true;

					CancelBuild();
				}

				// Cancel regardless of whether the user wants to stop the current build.
				// This method will be called again when the build finally stops if the exitAfterBuild field is set.
				e.Cancel = true;
			}

			if (!e.Cancel)
			{
				if (!CloseProject(forceSave))
					e.Cancel = true;
				else
					SaveSettings();
			}

			base.OnFormClosing(e);
		}

		private void manager_ProjectClosed(object sender, DocProjectEventArgs e)
		{
			serviceContainer.Remove(e.Project.Options);

			UninitializeProviderToolBars(e.Project.Provider);

			configurationToolStripComboBox.Items.Clear();
			platformToolStripComboBox.Items.Clear();
		}

		private void manager_BuildComplete(object sender, EventArgs e)
		{
			if (exitAfterBuild)
			{
				exitSilentlyAfterBuild = true;
				Application.Exit();
			}
			else
				UpdateProviderToolBars();
		}

		private void manager_IsDirtyChanged(object sender, EventArgs e)
		{
			SynchronizeUIForDirtyState();
			UpdateProviderToolBars();
		}

		private void environment_ComponentChanged(object sender, ComponentChangedEventArgs e)
		{
			manager.IsDirty = true;
		}

		private void buildSplitter_MouseDoubleClick(object sender, MouseEventArgs e)
		{
			ToggleBuildTrace();
		}

		private void propertyGrid_SelectedGridItemChanged(object sender, SelectedGridItemChangedEventArgs e)
		{
			SynchronizeResetItem(e.NewSelection);
		}

		private void propertyGrid_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
		{
			GridItem item = e.ChangedItem;

			if (item == propertyGrid.SelectedGridItem)
				SynchronizeResetItem(item);
		}

		private void descriptionToolStripMenuItem_Click(object sender, EventArgs e)
		{
			propertyGrid.HelpVisible = descriptionToolStripMenuItem.Checked;
		}

		private void resetToolStripMenuItem_Click(object sender, EventArgs e)
		{
			propertyGrid.ResetSelectedProperty();
		}

		private void optionsToolStripMenuItem_Click(object sender, EventArgs e)
		{
			using (OptionsDialog dialog = new OptionsDialog())
			{
				if (manager.Project != null)
					dialog.SelectedBuildEngineProviderName = manager.Project.Provider.Name;
				else
					dialog.SelectedBuildEngineProviderName = DocProjectEnvironment.DefaultBuildEngineProvider.Name;

				if (dialog.ShowDialog(this) == DialogResult.OK)
				{
					foreach (BuildEngineProvider provider in DocProjectEnvironment.BuildEngineProviders)
						provider.CommitProviderSettings();
				}
				else
				{
					foreach (BuildEngineProvider provider in DocProjectEnvironment.BuildEngineProviders)
						provider.CancelProviderSettings();
				}
			}
		}

		private void configurationToolStripComboBox_SelectedIndexChanged(object sender, EventArgs e)
		{
			if (!initializingBuildConfiguration)
				SynchronizeConfiguration();
		}

		private void platformToolStripComboBox_SelectedIndexChanged(object sender, EventArgs e)
		{
			if (!initializingBuildConfiguration)
				SynchronizePlatform();
		}

		private void buildTraceToolStripMenuItem_Click(object sender, EventArgs e)
		{
			ToggleBuildTrace();
		}

		private void buildToolStripButton_Click(object sender, EventArgs e)
		{
			Build();
		}

		private void cancelToolStripButton_Click(object sender, EventArgs e)
		{
			cancelToolStripButton.Enabled = false;

			cancelToolStripMenuItem.Visible = false;
			buildToolStripMenuItem.Visible = true;

			buildToolStripMenuItem.Enabled = false;

			CancelBuild();
		}

		private void buildHelpToolStripMenuItem_Click(object sender, EventArgs e)
		{
			Build();
		}

		private void cancelToolStripMenuItem_Click(object sender, EventArgs e)
		{
			cancelToolStripButton.Enabled = false;

			cancelToolStripMenuItem.Visible = false;
			buildToolStripMenuItem.Visible = true;

			buildToolStripMenuItem.Enabled = false;

			CancelBuild();
		}

		private void exitToolStripMenuItem_Click(object sender, EventArgs e)
		{
			Application.Exit();
		}

		private void openToolStripMenuItem_Click(object sender, EventArgs e)
		{
			OpenProject();
		}

		private void openToolStripButton_Click(object sender, EventArgs e)
		{
			OpenProject();
		}

		private void saveToolStripMenuItem_Click(object sender, EventArgs e)
		{
			SaveProject();
		}

		private void saveToolStripButton_Click(object sender, EventArgs e)
		{
			SaveProject();
		}

		private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
		{
			SaveProjectAs();
		}

		private void closeToolStripMenuItem_Click(object sender, EventArgs e)
		{
			CloseProject(false);
		}

		private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
		{
			using (AboutForm form = new AboutForm())
			{
				form.ShowDialog();
			}
		}

		private void buildItemsToolStripMenuItem_Click(object sender, EventArgs e)
		{
			ShowBuildItems();
		}

		private void rebuildHelpToolStripMenuItem_Click(object sender, EventArgs e)
		{
			Rebuild();
		}

		private void cleanHelpToolStripMenuItem_Click(object sender, EventArgs e)
		{
			Clean();
		}

		private void buildItemsToolStripButton_Click(object sender, EventArgs e)
		{
			ShowBuildItems();
		}

		private void buildTraceToolStripButton_Click(object sender, EventArgs e)
		{
			ToggleBuildTrace();
		}

		private void openInVisualStudioToolStripButton_Click(object sender, EventArgs e)
		{
			OpenInVisualStudio();
		}

		private void openInVisualStudioToolStripMenuItem_Click(object sender, EventArgs e)
		{
			OpenInVisualStudio();
		}
		#endregion

		#region Nested
		private sealed class ProgramServicesContainer : ServiceContainer
		{
			#region Public Properties
			#endregion

			#region Private / Protected
			private readonly IServiceProvider services;
			#endregion

			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="ProgramServicesContainer" /> class.
			/// </summary>
			public ProgramServicesContainer(IServiceProvider services)
			{
				if (services == null)
					throw new ArgumentNullException("services");

				this.services = services;
			}
			#endregion

			#region Methods
			protected override object GetService(Type serviceType)
			{
				object service = services.GetService(serviceType);

				if (service != null)
					return service;
				else
					return base.GetService(serviceType);
			}
			#endregion
		}
		#endregion
	}
}